package handler

import (
	"UserServer/model/database"
	"UserServer/rpc/internal/tool"
	"UserServer/rpc/pb"
	"context"
	"errors"
	"github.com/google/uuid"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"gorm.io/gorm"
	"time"
)

// 这个包就类似gozero的logic加上外部的userclient
// 它的userclient其实就是一个定义好的接口提供实现
// 只不过客户端的创建不是在这里，是在userclient，
// 我这里是写在了api的handler中，提供client，在api里面调用我的rpc客户端，从而实现更好的分离吧

type UserServer struct {
	pb.UnimplementedUserServer
}

// 这里注意 实现的方法的第一个参数一定是上下文，另一个参数就是请求，返回响应

func (UserServer) GetUser(ctx context.Context, request *pb.GetUserReq) (*pb.LoginAndRegisterResp, error) {
	user := &database.User{}
	if err := SC.DB.Model(&user).Where("uuid = ?", request.Uuid).First(&user).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, status.Errorf(codes.NotFound, "该uuid不存在")
		}
		SC.Logger.Error("数据库查询失败：", zap.Error(err))
		return nil, status.Errorf(codes.Internal, "数据库查询失败")
	}

	return &pb.LoginAndRegisterResp{
		Uuid:     user.UUID.String(),
		Username: user.Username,
		Phone:    user.Phone,
		Email:    user.Email,
		Sex:      user.Sex,
		Qq:       user.QQ,
		Id:       int32(user.ID),
	}, nil
}

func (UserServer) RegisterUser(ctx context.Context, request *pb.RegisterReq) (*pb.LoginAndRegisterResp, error) {
	// 1.先准备好这个需要的数据
	UUID := uuid.Must(uuid.NewV7())
	user := &database.User{
		Email:    request.Email,
		Password: tool.Bcrypt(request.Password1),
		UUID:     UUID,
	}

	// 2.判断是否有这个邮箱用户,如果不是未找到，就返回
	if err := SC.DB.Model(&user).Where("email = ?", request.Email).First(&user).Error; !errors.Is(err, gorm.ErrRecordNotFound) {
		SC.Logger.Info("未找到数据", zap.Error(err))
		return nil, status.Errorf(codes.AlreadyExists, "该邮箱已被注册")
	}

	// 3.如果没有，就完善user的信息写入数据库
	if err := SC.DB.Create(&user).Error; err != nil {
		SC.Logger.Error("写入数据库失败：", zap.Error(err))
		return nil, status.Errorf(codes.Internal, "写入数据库失败")
	}

	return &pb.LoginAndRegisterResp{
		Uuid:     user.UUID.String(),
		Username: user.Username,
		Phone:    user.Phone,
		Email:    user.Email,
		Sex:      user.Sex,
		Qq:       user.QQ,
	}, nil
}

func (UserServer) LoginUser(ctx context.Context, request *pb.LoginReq) (*pb.LoginAndRegisterResp, error) {
	user := &database.User{}
	if err := SC.DB.Model(&user).Where("phone = ?", request.Phone).First(&user).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			SC.Logger.Error("该手机号未找到：", zap.Error(err))
			return nil, status.Errorf(codes.NotFound, "该手机号未找到")
		}
		SC.Logger.Error("数据库查询失败：", zap.Error(err))
		return nil, status.Errorf(codes.Internal, "数据库查询失败")
	}

	// 判断加密之后的密码和数据库的密码是否相同
	if tool.Bcrypt(request.Password) != user.Password {
		return nil, status.Errorf(codes.Unknown, "密码输入错误")
	}

	return &pb.LoginAndRegisterResp{
		Uuid:     user.UUID.String(),
		Username: user.Username,
		Phone:    user.Phone,
		Email:    user.Email,
		Sex:      user.Sex,
		Qq:       user.QQ,
		Id:       int32(user.ID),
	}, nil
}

func (UserServer) EmailLogin(ctx context.Context, request *pb.EmailReq) (*pb.LoginAndRegisterResp, error) {
	user := &database.User{}
	if err := SC.DB.Model(&user).Where("email = ?", request.Email).First(&user).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			SC.Logger.Error("该邮箱未找到：", zap.Error(err))
			return nil, status.Errorf(codes.NotFound, "该邮箱未找到")
		}
		SC.Logger.Error("数据库查询失败：", zap.Error(err))
		return nil, status.Errorf(codes.Internal, "数据库查询失败")
	}
	// 判断加密之后的密码和数据库的密码是否相同
	if tool.Bcrypt(request.Password) != user.Password {
		return nil, status.Errorf(codes.Unknown, "密码输入错误")
	}
	return &pb.LoginAndRegisterResp{
		Uuid:     user.UUID.String(),
		Username: user.Username,
		Phone:    user.Phone,
		Email:    user.Email,
		Sex:      user.Sex,
		Qq:       user.QQ,
		Id:       int32(user.ID),
	}, nil
}

func (UserServer) Logout(ctx context.Context, request *pb.LogoutReq) (*pb.Response, error) {
	blackList := &database.JwtBlacklist{}
	// 1.删除缓存之中的token
	if err := SC.Cache.Del(ctx, request.Uuid).Err(); err != nil {
		SC.Logger.Error("缓存删除失败：", zap.Error(err))
		return nil, status.Errorf(codes.Internal, "缓存删除失败")
	}

	// 2.先写入缓存
	duration, _ := time.ParseDuration(request.Duration)
	if err := SC.Cache.Set(ctx, request.RefreshToken, "", duration).Err(); err != nil {
		SC.Logger.Error("写入缓存失败：", zap.Error(err))
		return nil, status.Errorf(codes.Internal, "写入缓存失败")
	}

	// 3.写入数据库
	blackList.Jwt = request.RefreshToken
	if err := SC.DB.Create(&blackList).Error; err != nil {
		SC.Logger.Error("写入数据库失败：", zap.Error(err))
		return nil, status.Errorf(codes.Internal, "写入数据库失败")
	}

	return nil, nil
}
